package domain

import (
	"context"

	"github.com/lab-online/internal/shared/code"
	"github.com/lab-online/internal/user/constant"
	"github.com/lab-online/internal/user/entity"
	"github.com/lab-online/pkg/auth"
)

func (d *Domain) ImportStudent(entities []entity.UserEntity) ([]entity.UserEntity, error) {
	length := len(entities)
	if length == 0 {
		return nil, constant.RESTErrStudentArrayEmpty
	}

	if err := d.checkImportStudentDuplicate(entities); err != nil {
		return nil, err
	}

	mergedUsers := make([]entity.UserEntity, 0, length)

	// Todo (pref): 比较一次/分批读取全部用户后再合并的效率
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	errs := make(chan error, length)
	results := make(chan entity.UserEntity, length)
	for _, user := range entities {
		go func(user entity.UserEntity) {
			select {
			case <-ctx.Done():
				return
			default:
				mergedUser, err := d.mergeSavedStudent(user)
				if err != nil {
					errs <- err
				} else {
					results <- mergedUser
				}
			}
		}(user)
	}

	for i := 0; i < length; i++ {
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		case err := <-errs:
			return nil, err
		case result := <-results:
			mergedUsers = append(mergedUsers, result)
		}
	}

	return d.repository.SaveManyUsers(mergedUsers)
}

// mergeSavedStudent 合并已存在的学生
func (d *Domain) mergeSavedStudent(student entity.UserEntity) (entity.UserEntity, error) {
	existsUser, dbErr := d.repository.GetUnscopedUserByUsername(student.GetUsername())
	switch {
	case dbErr != nil && !code.IsEqual(dbErr, code.DatabaseUserNotFound):
		return nil, dbErr
	case existsUser != nil && !existsUser.GetDeletedAt().Valid:
		mergedRole := existsUser.GetRole() | uint8(auth.RoleStudent)
		existsUser.Update(entity.WithRole(mergedRole))
		student = existsUser
	case existsUser != nil && existsUser.GetDeletedAt().Valid:
		student.Update(
			entity.WithID(existsUser.GetID()),
			entity.WithDeletedAt(existsUser.GetDeletedAt()),
		)
		fallthrough
	default:
		student.HashPassword()
		student.Update(entity.WithRole(uint8(auth.RoleStudent)))
	}

	return student, nil
}

// checkImportStudentDuplicate 检查学生学号是否重复
func (d *Domain) checkImportStudentDuplicate(users []entity.UserEntity) error {
	studentMap := make(map[string]bool, len(users))
	for _, user := range users {
		if v, ok := studentMap[user.GetUsername()]; ok && v {
			return constant.RESTErrStudentDuplicate
		}
		studentMap[user.GetUsername()] = true
	}
	return nil
}
